<?php

declare(strict_types=1);

namespace Phpben\Imi\ConfigCenter\Driver;

use RuntimeException;
use Imi\Config;
use Imi\Cache\CacheManager as Cache;
use Psr\SimpleCache\InvalidArgumentException;
use Yurun\Util\HttpRequest;

/**
 * 阿里云ACM驱动
 */
class AliyunAcm
{

    /**
     * 配置
     * @var array
     */
    protected array $config;

    /**
     * 时间戳
     * @var int|float
     */
    protected $time;


    public function __construct()
    {
        $this->time = round(microtime(true) * 1000);
        $this->config = Config::get('@app.beans.ConfigCenter.aliyun_acm');
        $this->client = new HttpRequest;
    }

    /**
     * 获取配置
     * @return array
     * @throws InvalidArgumentException
     */
    public function get(): array
    {
        return $this->response(['action' => 'config.co']);
    }

    /**
     * 更新配置
     * @param array $content 配置内容
     * @throws InvalidArgumentException
     */
    public function pull(array $content): void
    {
        $this->response(['action' => 'basestone.do?method=syncUpdateAll', 'params' => ['content' => json_encode($content, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)]]);
    }

    /**
     * 请求
     * @param array $config 请求配置
     * @return array
     * @throws InvalidArgumentException
     */
    protected function response(array $config): array
    {
        $sign = base64_encode(hash_hmac('sha1', $this->config['namespace'] . "+" . $this->config['group'] . "+" . $this->time, $this->config['secret_key'], true));
        $servers = Cache::get('so', 'aliyun_acm_servers');
        if (!$servers) {
            $response = $this->client->get("http://" . $this->config['endpoint'] . ":8080/diamond-server/diamond");
            if ($response->getStatusCode() !== 200) {
                throw new RuntimeException('AliyunAcm-Servers http request faild');
            }
            $servers = array_filter(explode("\n", (string)$response->getBody()));
            Cache::set('so', 'aliyun_acm_servers', $servers, 3600);
        }
        $server = $servers[array_rand($servers)];
        $action = $config['action'] == 'config.co' ? 'get' : 'post';
        $response = $this->client->headers([
            'Spas-AccessKey' => $this->config['access_key'],
            'timeStamp' => (string)$this->time,
            'Spas-Signature' => $sign,
            'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8',
        ])->$action("http://" . $server . ":8080/diamond-server/" . $config['action'], (array_merge([
            'tenant' => $this->config['namespace'],
            'dataId' => $this->config['data_id'],
            'group' => $this->config['group'],
        ], ($config['params'] ?? []))));
        if ($response->getStatusCode() !== 200) {
            throw new RuntimeException('AliyunAcm http request statuc code is not 200');
        }
        $content = (string)$response->getBody();
        if (!$content) {
            throw new RuntimeException("AliyunAcm http request body faild");
        }
        $json = json_decode($content, true);
        if ($action == 'post' && (!isset($json['code']) || !$json['code'] == 200)) {
            throw new RuntimeException($json['message'] ?? "AliyunAcm http request faild");
        }
        return $json;
    }

}